/*-*- coding:utf-8; mode:java-mode -*-*/
/*
	HTTPサーバ
		tama
*/
package net.cattaka.teamredwhite;

import java.net.Socket;
import java.net.ServerSocket;
import java.net.NetworkInterface;
import java.net.InetAddress;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.OutputStreamWriter;
import java.util.Map;
import java.util.HashMap;
import java.util.Enumeration;

public class HTTPServer
{
	/** ログ出力タグ */
	private String _TAG = "HTTPServer";

	/** 標準待ち受けポート */
	static final public int DEFAULT_PORT = 8080;
	/** 待ち受けポート */
	private int mPort;
	/** サーバソケット */
	private ServerSocket mServerSocket;
	/** 待ち受けスレッド */
	private Thread mListenThread;
	/** リクエストハンドラのマップ */
	private Map<String,HTTPHandler> mHandlerMap = new HashMap<String,HTTPHandler>();

	/**
	 * HTTPServerコンストラクタ
	 * @param	port	待ち受けポート
	 */
	public HTTPServer(int port) {
		mPort = port;
	}

	/**
	 * HTTPServerコンストラクタ
	 */
	public HTTPServer() {
		this(DEFAULT_PORT);
	}

	/**
	 * 待ち受け開始
	 */
	public void listen() {
		if (mListenThread != null) {
			android.util.Log.e(_TAG, "mListenThread != null");
			//throw;
		}

		// スレッド起こす
		mListenThread = new Thread(new Runnable() {
				public void run() {
					try {
						mServerSocket = new ServerSocket(mPort);
						listenProc();
					} catch (IOException e) {
						// 中断された?
						android.util.Log.d(_TAG, e.toString());
					}
				}
			});
		mListenThread.start();
	}

	/**
	 * 待ち受けを閉じる
	 */
	public void close() {
		if (mServerSocket != null) {
			try {
				mServerSocket.close();
			} catch (IOException e) {
			}
			mServerSocket = null;
		}
		mListenThread = null;
	}

	/**
	 * 待ち受けプロシジャ
	 */
	private void listenProc() throws IOException {
		while (true) {
			final Socket client = mServerSocket.accept();
			if (client == null) {
				// こ、この方法はダサい...
				break;
			}
			new Thread(new Runnable() {
					public void run() {
						try {
							handleRequest(client);
						} catch (IOException e) {
							android.util.Log.e(_TAG, e.toString());
						} finally {
							try {
								client.close();
							} catch (IOException e) {
								android.util.Log.e(_TAG, e.toString());
							}
						}
					}
				}).start();
		}
	}

	/**
	 * リクエストのハンドル
	 * @param	client	クライアントソケット
	 */
	private void handleRequest(Socket client) throws IOException {
		InputStream in = client.getInputStream();
		BufferedReader stdin = new BufferedReader(new java.io.InputStreamReader(in));

		// ヘッダ解析 - リクエストライン
		String token[] = stdin.readLine().split(" ");	// 簡易処理!
		String method = token[0];
		String uri = token[1];

		// パスの分解
		String tmp[] = uri.split("\\?");
		String path = tmp[0];
		String query = (tmp.length > 1) ? tmp[1] : "";

		// クエリの分解
		Map<String,String> param = new HashMap<String,String>();
		String params[] = query.split("&");
		for (int index = 0; index < params.length; index++) {
			tmp = params[index].split("=");
			String name = tmp[0];
			String value = (tmp.length > 1) ? tmp[1] : "";
			// ここでURLエンコードをデコード
			param.put(name, java.net.URLDecoder.decode(value));
		}

		// リクエストライン以外のヘッダは無視
		while (!"".equals(stdin.readLine()));
		// 残りはハンドラに委ねる

		// ハンドラを呼ぶ
		if (mHandlerMap.containsKey(path)) {
			mHandlerMap.get(path).handle(path, param, in, client.getOutputStream());
		} else {
			// 見つからないよ!
			OutputStreamWriter output = new OutputStreamWriter(client.getOutputStream());
			output.write("HTTP/1.0 404 Not Found\r\n");
			output.write("Content-Type: text/plain\r\n");
			output.write("\r\n");
			output.write("Not Found");
			output.flush();
		}
	}

	/**
	 * リクエストハンドラ追加
	 * @param	path	パス
	 * @param	handler	ハンドラ
	 */
	public void addHandler(String path, HTTPHandler handler) {
		mHandlerMap.put(path, handler);
	}

	public interface HTTPHandler {
		/**
		 * リクエストハンドラ
		 * @param	path	リクエストされたURLパス
		 * @param	param	クエリパラメタの辞書(Map)
		 * @param	stdin	入力ストリーム(POSTの場合のみ。POST以外では null)
		 * @param	stdout	出力ストリーム(AS ISでクライアントソケットに出力される)
		 */
		public void handle(String path, Map<String,String> param, InputStream stdin, OutputStream stdout) throws IOException;
	}


	// http://d.hatena.ne.jp/d-kami/20100803/1280819590 より
	public static String getIPAddress() {
		try {
			Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
			while(interfaces.hasMoreElements()){
				NetworkInterface network = interfaces.nextElement();
				Enumeration<InetAddress> addresses = network.getInetAddresses();
				while(addresses.hasMoreElements()){
					String address = addresses.nextElement().getHostAddress();
					//127.0.0.1と0.0.0.0以外のアドレスが見つかったらそれを返す
					if(!"127.0.0.1".equals(address) && !"0.0.0.0".equals(address)){
						return address;
					}
				}
			}
		} catch (IOException e) {
			// NOP
		}
		return "127.0.0.1";
	}
}
